package top.mydb.hejianwei.dm.pageIndex;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import top.mydb.hejianwei.dm.page.Page; // 引入ByteBuffer
/**
 * 页面索引
 * <p>
 * PageIndex结构：
 * [FreeSpaceAmount] short
 */
public class PageIndex {
    // 一个条目(Entry)的大小，short类型占2字节
    private static final int PI_ENTRY_SIZE = 2;
    // 页面索引元数据在页面中的偏移量，我们从40字节后开始存
    private static final int PI_ENTRY_OFFSET = 40; 

    private Lock lock;
    // 所有的页面信息，内存中缓存
    private List<PageInfo> list;

    public PageIndex() {
        lock = new ReentrantLock();
        list = new ArrayList<>();
    }

    /**
     * 从一个普通的数据页中，解析出页面索引的信息
     * @param pg Page对象
     * @return 页面索引列表
     */
    public static List<PageInfo> parsePageIndex(Page pg) {
        byte[] raw = pg.getData();
        // 用一个ByteBuffer来包装页面的原始数据
        ByteBuffer buf = ByteBuffer.wrap(raw);
        List<PageInfo> result = new ArrayList<>();

        // 从页面的固定偏移量开始读取
        buf.position(PI_ENTRY_OFFSET);
        while (true) {
            // 每次读取一个条目（2字节的short）
            short freeSpace = buf.getShort();
            if (freeSpace == 0) {
                // 读取到0表示条目结束
                break;
            }
            result.add(new PageInfo(0, freeSpace)); // 页号是隐含的，这里先填0
        }
        return result;
    }

    /**
     * 将内存中的页面索引信息，序列化写回数据页
     * @param pg Page对象
     * @param piList 页面索引列表
     */
    public static void flushPageIndex(Page pg, List<PageInfo> piList) {
        byte[] raw = pg.getData();
        ByteBuffer buf = ByteBuffer.wrap(raw);

        // 从页面的固定偏移量开始写入
        buf.position(PI_ENTRY_OFFSET);
        for (PageInfo pi : piList) {
            buf.putShort((short) pi.freeSpace);
        }
        // 在末尾写入一个0作为结束符
        buf.putShort((short) 0);

        // 将修改同步到页面对象
        pg.setDirty(true);
    }

    /**
     * 选择一个空闲空间足够的页面
     * @param spaceSize 需要的空间大小
     * @return PageInfo对象
     */
    public PageInfo select(int spaceSize) {
        lock.lock();
        try {
            // 从后向前遍历，优先使用页号大的页面，有助于数据页的紧凑
            for (int i = list.size() - 1; i >= 0; i--) {
                PageInfo pi = list.get(i);
                if (pi.freeSpace >= spaceSize) {
                    return pi;
                }
            }
            // 没有找到合适的页面
            return null;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 当一个新页面被创建时，向索引中添加一条信息
     * @param pgno 新页面的页号
     * @param freeSpace 新页面的初始空闲空间
     */
    public void add(int pgno, int freeSpace) {
        lock.lock();
        try {
            list.add(new PageInfo(pgno, freeSpace));
        } finally {
            lock.unlock();
        }
    }

    // 更新指定页面的空闲空间
    public void update(int pgno, int freeSpace) {
        lock.lock();
        try {
            // 在list中找到对应的PageInfo并更新
            for(PageInfo pi : list) {
                if(pi.pgno == pgno) {
                    pi.freeSpace = freeSpace;
                    break;
                }
            }
        } finally {
            lock.unlock();
        }
    }

    // 获取内部列表，用于刷盘
    public List<PageInfo> getPageInfoList() {
        return list;
    }
}